﻿#include "audioDecode.h"
#include <QAudio>     //这五个是QT处理音频的库
#include <QAudioFormat>
#include <QAudioInput>
#include <QAudioOutput>
#include "videoPlayer.h"

audioDecode::audioDecode(QObject *parent) :
    QThread(parent)
{

}


audioDecode::~audioDecode()
{

}

//音频输出初始化
void audioDecode::Audio_Out_Init()
{
    qDebug() << tr("audioDecode::Audio_Out_Init():  开始音频输出初始化.\n");
    QAudioFormat auido_out_format;
    //设置录音的格式
    auido_out_format.setSampleRate(44100); //设置采样率以对赫兹采样。 以秒为单位，每秒采集多少声音数据的频率.
    auido_out_format.setChannelCount(1);   //将通道数设置为通道。
    auido_out_format.setSampleSize(16);     /*将样本大小设置为指定的sampleSize（以位为单位）通常为8或16，但是某些系统可能支持更大的样本量。*/
    auido_out_format.setCodec("audio/pcm"); //设置编码格式
    auido_out_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序
    auido_out_format.setSampleType(QAudioFormat::SignedInt); //样本类型
    //获取音频输出设备信息
    audio_output_config.audio=QAudioDeviceInfo::defaultOutputDevice();
    qDebug()<<"系统默认声卡:"<<audio_output_config.audio.deviceName();
    if(audio_output_config.audio.isNull())
    {
        qDebug()<< tr("audioDecode::Audio_Out_Init():   没有音频输出设备, 终止初始化!");
        return;
    }
    QAudioDeviceInfo info(audio_output_config.audio);
    if(audio_out)
    {
        delete audio_out;
        audio_out=Q_NULLPTR;
    }
    audio_out = new QAudioOutput(info,auido_out_format);
    audio_out_streamIn=audio_out->start();
    qDebug() << tr("audioDecode::Audio_Out_Init():  音频输出初始化成功.\n");
}

void audioDecode::Audio_Out_Delete()
{
    delete audio_out;
    audio_out = Q_NULLPTR;
}

void audioDecode::decodeParamInit()
{
    qDebug()<<"audioDecode::decodeParamInit():   初始化音频解码参数 ";
    videoPlayer* parent = (videoPlayer *)this->parent();
    if(parent == Q_NULLPTR)
    {
        qDebug()<<"audioDecode::decodeParamInit():   decodeParamInit() 失败 !";
    }
    this->video_file = parent->getVideoName();
    this->audioDecodeParam.Decoding = parent->getDecodingRelated();//获取初始初始化好的解码器信息
    this->audioDecodeParam.UIPlay = parent->getPlayRelated();

    //初始化音频设备
    Audio_Out_Init();
    //设置音频转码后输出相关参数
    //采样的布局方式
    this->audioDecodeParam. out_channel_layout = AV_CH_LAYOUT_MONO; //单声道音频布局
    //采样个数
    this->audioDecodeParam. out_nb_samples = MAX_AUDIO_FRAME_SIZE;
    //采样格式
    this->audioDecodeParam. sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    this->audioDecodeParam. out_sample_rate = 44100;
    //通道数
    this->audioDecodeParam. out_channels=0;

    //初始化音频解码相关的参数
    this->audioDecodeParam. PCM_pFrame = Q_NULLPTR;
    this->audioDecodeParam. PCM_pFrame = av_frame_alloc();// 存放解码后PCM数据的缓冲区

    //创建packet,用于存储解码前音频的数据
    this->audioDecodeParam. packet = (AVPacket *)malloc(sizeof(AVPacket));
    av_init_packet(this->audioDecodeParam. packet);

    //通道数
    this->audioDecodeParam. out_channels = av_get_channel_layout_nb_channels(this->audioDecodeParam.
                                                                             out_channel_layout);
    //得到每帧音频数据大小
    this->audioDecodeParam. buffer_size = av_samples_get_buffer_size(nullptr,
                                             this->audioDecodeParam. out_channels,
                                             this->audioDecodeParam. out_nb_samples,
                                             this->audioDecodeParam. sample_fmt, 1);

    //创建buffer,存放转码后的数据
    this->audioDecodeParam. buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);

    this->audioDecodeParam. in_channel_layout = \
            av_get_default_channel_layout(this->audioDecodeParam.Decoding->format_ctx->streams[this->audioDecodeParam.Decoding->audioStreamIndex]->codec->channels);

    //设置转码参数
    this->audioDecodeParam. convert_ctx = swr_alloc_set_opts(nullptr,
                                     this->audioDecodeParam. out_channel_layout,
                                     this->audioDecodeParam. sample_fmt,
                                     this->audioDecodeParam. out_sample_rate,
                                     this->audioDecodeParam. in_channel_layout,
                                     this->audioDecodeParam.Decoding->format_ctx->streams[this->audioDecodeParam.Decoding->audioStreamIndex]->codec->sample_fmt,
                                     this->audioDecodeParam.Decoding->format_ctx->streams[this->audioDecodeParam.Decoding->audioStreamIndex]->codec->sample_rate,
                                     0,
                                     nullptr);

    //转码后的数据
    qDebug() << (tr("转码_nb_samples=%1\n").arg(this->audioDecodeParam. out_nb_samples)); //此帧描述的音频样本数(每通道
    qDebug() << (tr("转码_音频数据声道=%1\n").arg(this->audioDecodeParam. out_channels));      //声道数量
    qDebug() << (tr("转码_音频数据采样率=%1\n").arg(this->audioDecodeParam. out_sample_rate)); //采样率
    qDebug() << (tr("转码_channel_layout=%1\n").arg(this->audioDecodeParam. out_channel_layout)); //通道布局

    //初始化转码器
    swr_init(this->audioDecodeParam. convert_ctx);

    //开始解码音频数据
    this->audioDecodeParam. temp_audio_clock=0; //音频时钟
    qDebug() << (tr("audioDecode::decodeParamInit():  解码参数初始化完成!")); //通道布局
}

void audioDecode::decodeParamDelete()
{
    this->Audio_Out_Delete();
    av_frame_free( &(this->audioDecodeParam. PCM_pFrame) );
    this->audioDecodeParam. PCM_pFrame = Q_NULLPTR;
    free(this->audioDecodeParam. packet);
    this->audioDecodeParam. packet = Q_NULLPTR;
    av_free(this->audioDecodeParam. buffer);
    this->audioDecodeParam. buffer = Q_NULLPTR;
}

int audioDecode::threadStartUp()
{
    this->decodeParamInit();
    this->start();
    return 0;
}
int audioDecode::threadStop()
{
    this->decodeParamDelete();
    return 0;
}

void audioDecode::run()
{
    int re;
    bool *videoFileEnd = &(this->audioDecodeParam.UIPlay->threadControl.videoFileEnd);
    AVFormatContext* format_ctx = this->audioDecodeParam.Decoding->format_ctx;
    int audioStreamIndex = this->audioDecodeParam.Decoding->audioStreamIndex;
    AVPacket* pkt = &(this->audioDecodeParam.pkt);
    AVFrame *PCM_pFrame = this->audioDecodeParam.PCM_pFrame;
    struct SwrContext *convert_ctx = this->audioDecodeParam.convert_ctx;
    uint8_t *buffer = this->audioDecodeParam.buffer;
    qint64 *temp_audio_clock = &(this->audioDecodeParam.temp_audio_clock);
    double *audio_clock = &(this->audioDecodeParam.Decoding->audio_clock); //当前音频时钟位置
    int buffer_size = this->audioDecodeParam.buffer_size;
    while(1)
    {
        switch (this->audioDecodeParam.UIPlay->threadControl.runningStat)
        {
        case PLAY_STOP:
        {
            this->audioDecodeParam.UIPlay->threadSync.audioThreadStat = PLAY_STOP;
            goto END;
        }
            break;
        case PLAY_RUNNING:
        {
            this->audioDecodeParam.UIPlay->threadSync.audioThreadStat = PLAY_RUNNING;
            //判断队列里是否有数据,没有数据就等待一下
            if(this->audioDecodeParam.Decoding->videoAudioAVPacketQueue .get_audio_pack_cnt() <= 0)
            {
                //判断文件是否读取完毕
                if(*videoFileEnd)
                {
                    qDebug()<<tr("audioDecode::run():   文件读取完毕, 音频队列数据处理完毕, 结束音频解码线程!");
                    break;
                }
                qDebug() << tr("audioDecode::run():   音频队列中没有数据了, 等待一下!");
                msleep(10);
                continue;
            }

            //取出一帧数据
            //qDebug()<<"audioDecode::run():  从队列中取出一帧数据";
            *pkt = this->audioDecodeParam.Decoding->videoAudioAVPacketQueue .read_audio_pack();

            //发送将要解码的数据帧
             re = avcodec_send_packet(format_ctx->streams[audioStreamIndex]->codec,pkt);
             if (re != 0)
             {
                 av_packet_unref(pkt);//不成功就释放这个pkt
                 continue;
             }

             //接受后对数据帧进行解码
             re = avcodec_receive_frame(format_ctx->streams[audioStreamIndex]->codec, PCM_pFrame);
             if (re != 0)
             {
                 av_packet_unref(pkt);//不成功就释放这个pkt
                 continue;
             }

             //转码  针对每一帧音频的处理。把一帧帧的音频作相应的重采样
            swr_convert(convert_ctx, &buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)PCM_pFrame->data, PCM_pFrame->nb_samples);

            //判断是否是正常的时间戳
            if (pkt->pts != AV_NOPTS_VALUE)
            {
                //计算当前帧再视频中的时间位置
                qDebug()<<"音频帧显示的时间戳:"<<PCM_pFrame->pts*av_q2d(format_ctx->streams[audioStreamIndex]->time_base);
                *audio_clock = av_q2d(format_ctx->streams[audioStreamIndex]->time_base) * PCM_pFrame->pts;
                //qDebug()<<"audio_clock:"<<*audio_clock;
                //向主线程传递音频时间
                if(*temp_audio_clock != (qint64)(*audio_clock))
                {
                    *temp_audio_clock=(qint64)*audio_clock;
                }
            }
            if(!audio_output_config.audio.isNull())
            {
                //音频播放
                while(audio_out_streamIn->write((const char *)buffer,buffer_size)!=buffer_size)
                {

                }
            }
        }
            break;
        case PLAY_PAUSE:
        {
            this->audioDecodeParam.UIPlay->threadSync.audioThreadStat = PLAY_PAUSE;
            msleep(100); //暂停播放
        }
            break;
        case PLAY_SEEK:
        {
            this->audioDecodeParam.UIPlay->threadSync.audioThreadStat = PLAY_SEEK;
            while(PLAY_SEEK == this->audioDecodeParam.UIPlay->threadSync.playerThreadStat) //等待player先执行 PLAY_SEEK 相关代码
            {
                msleep(10);
            }
        }
            break;
        case PLAY_FINISHED:
        {
            this->audioDecodeParam.UIPlay->threadSync.audioThreadStat = PLAY_FINISHED;
            msleep(100); //暂停播放
        }
            break;
        } //switch (this->audioDecodeParam.UIPlay->threadControl.runningStat)

    }// while(1)

END:
    this->threadStop();
    qDebug() << tr("音频解码线程退出成功!");
}
